package summaries;

import java.util.ArrayList;
import java.util.List;

import utils.Pair;

import linguistic.Qualifier;
import linguistic.Quantifier;
import linguistic.Summarizer;
import linguistic.Variable;

public class Generator {

	private List<Variable> variables;
	private List<Quantifier> quantifiers;
	
	private List<Summary> summaries;
	
	public Generator() {
		quantifiers = new ArrayList<>();
		variables = new ArrayList<>();
		summaries = new ArrayList<>();
	}
	
	public void addQuantifier(Quantifier quantifier) {
		quantifiers.add(quantifier);
	}
	
	public void addVariable(Variable variable) {
		variables.add(variable);
	}
	
	public List<Summary> getSummaries() {
		return summaries;
	}
	
	public void generate() {
		if (quantifiers.size() == 0) {
			System.out.println("Any quantifiers has not been defined!");
			return;
		}
		
		if (variables.size() == 0) {
			System.out.println("Any Linguistic Variable has not been defined!");
		}
		
		// Copy all active fuzzy sets from all linguistic variables into one list
		List<Summarizer> summarizersForSummary = new ArrayList<>();
		
		for (Variable variable : variables) {
			for (int i = 0; i < variable.getFuzzySets().size(); ++i) {
				if (variable.getActive().get(i)) {		// If set is active, append it to sets for use
					Summarizer summarizer = new Summarizer();
					summarizer.setFuzzySet( variable.getFuzzySets().get(i));
					summarizer.setName(variable.getLabels().get(i));
					summarizer.setColumn(variable.getColumn());
					summarizersForSummary.add(summarizer);
				}
			}
		}
		
		// Now lets compute all possible summaries
		int z = summarizersForSummary.size();
		int k = quantifiers.size();
		int m = 0;
		for (int i = 0; i <= z-1; ++i) {
			m += (factorial(z) / (factorial(i) * factorial(z - i))) * ((int)Math.pow(2, z-i) - 1);
		}
		m *= k;
		System.out.println("There is " + m + " possible sumamries.");
		
		// Finally lets start generating combinations
		List<Pair<Integer, Integer>> combinations = new ArrayList<>();
		
		for (int i = 0; i < z; ++i) {
			for (int j = 1; j <= z - i; ++j) {
				//System.out.println(i + " " + j);
				Pair<Integer, Integer> pair = new Pair<>(i, j);
				combinations.add(pair);
			}
		}
		
		for (int i = 0; i < combinations.size(); ++i) {
			int w_no = combinations.get(i).first();
			int s_no = combinations.get(i).second();
			
			CombinationGenerator w_generator = new CombinationGenerator(z, w_no);
			CombinationGenerator s_generator = new CombinationGenerator(z, s_no);
			
			List<List<Integer>> w_values = new ArrayList<>();
			List<List<Integer>> s_values = new ArrayList<>();
			
			while (w_generator.hasMore()) {
				int[] w_combination = w_generator.getNext();
				List<Integer> w_local = new ArrayList<>();
				for (int y = 0; y < w_combination.length; ++y) {
					w_local.add(w_combination[y]);
				}
				if (w_local.size() != 0) {
					w_values.add(w_local);
				}
			}
			
			while (s_generator.hasMore()) {
				int[] s_combination = s_generator.getNext();
				List<Integer> s_local = new ArrayList<>();
				for (int y = 0; y < s_combination.length; ++y) {
					s_local.add(s_combination[y]);
				}
				s_values.add(s_local);
			}
			
			// Create last lists with combinations (remove duplicates)
			Summary summary = null;
			if (w_values.size() == 0) {		// in this case, there is no qualifiers, only summarisers.
				for (int j = 0; j < s_values.size(); ++j) {
					for (int l = 0; l < s_values.get(j).size(); ++l) {
						summary = new Summary();
						for (int n = 0; n < s_values.get(j).size(); ++n) {
							summary.addSummarizer(summarizersForSummary.get(s_values.get(j).get(n)));
						}
					}
					// Append summary without quantifier!
					summaries.add(summary);
				}
			} else {						// now is more complicated, in this case qualifiers exists
				for (int j = 0; j < w_values.size(); ++j) {
					for (int l = 0; l < s_values.size(); ++l) {
						if (!hasAny(w_values.get(j), s_values.get(l))) {
							summary = new Summary();
							// Append qualifiers
							for (int n = 0; n < w_values.get(j).size(); ++n) {
								Qualifier qualifier = new Qualifier(summarizersForSummary.get(w_values.get(j).get(n)));
								summary.addQualifier(qualifier);
							}
							// Append summarisers
							for (int n = 0; n < s_values.get(l).size(); ++n) {
								Summarizer summarizer = summarizersForSummary.get(s_values.get(l).get(n));
								summary.addSummarizer(summarizer);
							}
							// Append summary without quantifier!
							summaries.add(summary);
						}
					}
				}
			}
		}
		summaries = appendQuantifiers(summaries);
	}
	
	
	// Internal tools
	private int factorial(int n) {
		int f = 1;
		
		if (n == 0) {
			f = 1;
		} else if (n == 1) {
			f = 1;
		} else {
			for (int i = 1; i <= n; ++i) {
				f *= i;
			}
		}
		return f;
	}
	
	private boolean hasAny(List<Integer> a, List<Integer> b) {
		for (Integer valueA : a) {
			for (Integer valueB : b) {
				if (valueA == valueB) {
					return true;
				}
			}
		}
		return false;
	}
	
	private List<Summary> appendQuantifiers(List<Summary> summaries) {
		List<Summary> finalSummaries = new ArrayList<>();
		for (Quantifier quantifier : quantifiers) {
			for (Summary summary : summaries) {
				Summary s = new Summary(summary);
				s.addQuantifier(quantifier);
				finalSummaries.add(s);
			}
		}
		return finalSummaries;
	}
}
